/*
 * Copyright (C) 2016 mzlion(and.mz.yq@gmail.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mzlion.easyokhttp.response;

import com.mzlion.core.io.FileUtils;
import com.mzlion.core.io.IOUtils;
import com.mzlion.core.json.TypeRef;
import com.mzlion.core.json.gson.JsonUtil;
import com.mzlion.core.lang.Assert;
import com.mzlion.easyokhttp.HttpClientException;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.internal.Util;

import java.io.*;

/**
 * <p>
 * Http响应结果处理类，通过{@linkplain #isSuccess()}可以得知服务器是否是200返回。
 * 并且该类提供了将结果转为其它对象的便捷方法
 * </p>
 *
 * @author mzlion on 2016/4/17
 */
public class HttpResponse {

    private Response rawResponse;

    /* 请求是否成功 */
    private boolean isSuccess;

    /* 请求失败时错误消息 */
    private String errorMessage;

    private transient byte[] data;
    private transient boolean hasClosed;

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean success) {
        isSuccess = success;
    }

    /**
     * 请求失败时错误消息
     *
     * @return 失败消息
     */
    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public Response getRawResponse() {
        return rawResponse;
    }

    public void setRawResponse(Response rawResponse) {
        this.rawResponse = rawResponse;
    }

    /**
     * 将响应结果转为字符串
     *
     * @return 响应结果字符串
     * @throws HttpClientException 如果服务器返回非200则抛出此异常
     */
    public String asString() {
        MediaType mediaType = this.rawResponse.body().contentType();
        if (!this.hasClosed) this.asByteData();
        return new String(this.data, mediaType == null ? Util.UTF_8 : mediaType.charset(Util.UTF_8));
    }

    /**
     * 将响应结果转为JavaBean对象
     *
     * @param targetClass 目标类型
     * @param <E>         泛型类型
     * @return JavaBean对象
     * @throws HttpClientException 如果服务器返回非200则抛出此异常
     */
    public <E> E asBean(Class<E> targetClass) {
        String responseStr = this.asString();
        return JsonUtil.fromJson(responseStr, targetClass);
    }

    /**
     * 将响应结果转为JavaBean对象
     * <p>
     * 用法如下：Map&lt;String,String&gt; data = httpResponse.asBean(new TypeRef&lt;Map&lt;String,String&gt;&gt;);
     * </p>
     *
     * @param typeRef 带有泛型类的封装类
     * @param <E>     泛型类型
     * @return JavaBean对象
     * @throws HttpClientException 如果服务器返回非200则抛出此异常
     */
    public <E> E asBean(TypeRef<E> typeRef) {
        String responseStr = this.asString();
        return JsonUtil.fromJson(responseStr, typeRef);
    }

    /**
     * 将响应结果转为字节数组
     *
     * @return 字节数组
     * @throws HttpClientException 如果服务器返回非200则抛出此异常
     */
    public byte[] asByteData() {
        if (!this.isSuccess) throw new HttpClientException(this.errorMessage);
        if (this.hasClosed) return this.data;
        try {
            this.data = this.rawResponse.body().bytes();//okhttp3自动关闭流
            this.hasClosed = true;
            return this.data;
        } catch (IOException e) {
            throw new HttpClientException(e);
        }
    }

    /**
     * 将响应结果输出到文件中
     *
     * @param saveFile 目标保存文件,非空
     */
    public void transferToFile(File saveFile) {
        if (!this.isSuccess) throw new HttpClientException(this.errorMessage);
        Assert.notNull(saveFile, "The file is null.");
        FileOutputStream out = null;
        InputStream in = null;
        try {
            in = this.rawResponse.body().byteStream();
            out = FileUtils.openFileOutputStream(saveFile);
            IOUtils.copy(in, out);
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }
    }

    /**
     * 将响应结果输出到输出流,并不会主动关闭输出流{@code out}
     *
     * @param out 输出流,非空
     */
    public void transferToStream(OutputStream out) {
        if (!this.isSuccess) throw new HttpClientException(this.errorMessage);
        Assert.notNull(out, "OutputStream is null.");
        try {
            IOUtils.copy(this.rawResponse.body().byteStream(), out);
        } finally {
            this.rawResponse.close();
        }
    }
}
